package com.lanyou.esb.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.lanyou.cook.common.Constants;
import com.lanyou.esb.common.CommonUtils;
import com.lanyou.esb.common.Constants4Redis;
import com.lanyou.esb.datadictionary.AuditStatusType;
import com.lanyou.esb.datadictionary.AuthencationModeType;
import com.lanyou.esb.datadictionary.DataFormateType;
import com.lanyou.esb.datadictionary.EnableType;
import com.lanyou.esb.datadictionary.IfOperationType;
import com.lanyou.esb.datadictionary.IfParameterType;
import com.lanyou.esb.datadictionary.IfReqType;
import com.lanyou.esb.datadictionary.IfType;
import com.lanyou.esb.datadictionary.ParameterType;
import com.lanyou.esb.entity.Http;
import com.lanyou.esb.entity.IfParameter;
import com.lanyou.esb.redis.proxy.impl.ServiceUsabilityJedisProxy;
import com.lanyou.esb.repository.HttpRepository;
import com.lanyou.esb.vo.HttpVo;
import com.lanyou.esb.web.param.HttpInterfaceParam;
import com.lanyou.esb.web.param.ServiceTestingParam;

/**
 * 服务可用性检测
 * 
 * @author Davey.wu
 */
@Service
public class ServiceUsabilityService {

	@Autowired
	private HttpService httpInterfaceService;

	@Autowired
	ServiceTestingService serviceTestingService;

	@Autowired
	private HttpRepository httpRepository;

	@Autowired
	private ServiceUsabilityJedisProxy serviceUsabilityJedisProxy;
	//
	// private static ExecutorService executorService;

	private static final FastDateFormat FASTDATEFORMAT_YYYYMMDDHHMMSS = FastDateFormat
			.getInstance(Constants.DATETIME_PATTERN_1);

	/** 分页大小 **/
	private static final int PAGE_SIZE = 25;
	/** 间隔休眠时间 **/
	// private static final int SLEEP_TIME = 1000;
	/** 检测结果列表 **/
	private static List<String[]> TEST_RESULT_COLLECTION = new ArrayList<>(
			PAGE_SIZE);

	/**
	 * 初始化线程池
	 */
	@PostConstruct
	public void init() {
		// executorService = Executors.newCachedThreadPool();
	}

	/**
	 * 定时检测接口是否可用
	 */
	@Scheduled(cron = "0 0/30 * * * ?")
	public void check() {
		// 获取接口总数
		long count = httpRepository.count();
		// 当前页
		int currentPage = 0;
		// 接口MAP
		Map<String, Http> httpsMap = null;
		// 参数列表
		//List<IfParameter> parameterList = null;
		// 循环获取接口,每次取固定数据的接口
		while (currentPage * PAGE_SIZE < count) {
			// 获取接口
			httpsMap = httpInterfaceService.getHttpInterfaceInfos(null,
					AuditStatusType.AUDITED.ValueCode,
					EnableType.ENABLE.ValueCode,
					IfOperationType.QUERY.ValueCode, currentPage, PAGE_SIZE);
			if (httpsMap == null || httpsMap.size() == 0) {
				break;
			}
			// 获取参数
//			parameterList = httpInterfaceService.getInterfaceParameter(
//					Arrays.asList(httpsMap.keySet().toArray(
//							new String[httpsMap.keySet().size()])),
//					ParameterType.REQUEST.ValueCode);
//			if (parameterList != null && parameterList.size() > 0) {
//				for (IfParameter parameter : parameterList) {
//					httpsMap.get(parameter.getHttp().getId()).addParameter(
//							parameter);
//				}
//				// 清除参数列表
//				parameterList.clear();
//			}
			// 遍历所有接口，并进行测试
			loop(httpsMap.values());
			httpsMap.clear();
			currentPage++;

			// try {
			// // 为减少资源竞争，等待1秒钟
			// Thread.currentThread().sleep(SLEEP_TIME);
			// } catch (InterruptedException e) {
			// // TODO Auto-generated catch block
			// e.printStackTrace();
			// }
		}
	}

	/**
	 * 获取服务可用性检测结果<br>
	 * 功能描述：<br>
	 * 
	 * @param param
	 *            ：查询条件
	 * @param systemIds
	 *            ：当前用户可访问的系统标识列表
	 * @param pageable
	 *            ：分页对象
	 * @return
	 */
	public Page<HttpVo> getCheckResult(HttpInterfaceParam param,
			List<String> systemIds, Pageable pageable) {
		// 当前用户无可见的系统，返回空集
		if (systemIds == null || systemIds.size() == 0) {
			return new PageImpl<HttpVo>(new ArrayList<>(0), pageable, 0);
		}
		// 根据查询条件，获取REDIS键值
		String key = getKey(param);
		// 根据键值从REDIS中获取数据
		Map<String, String> map = serviceUsabilityJedisProxy.hgetAll(key);
		// 无数据，返回空集
		if (map == null || map.size() == 0) {
			return new PageImpl<HttpVo>(new ArrayList<>(0), pageable, 0);
		}
		// 获取符合条件的HTTP接口标识集合
		List<String> ifIds = httpInterfaceService.getHttpIdBySystemId(
				systemIds, param.getFieldId(), param.getFieldSystemId(),
				param.getName(), AuditStatusType.AUDITED.ValueCode);
		if (ifIds == null) {
			ifIds = new ArrayList<>(0);
		}
		Map<String, Boolean> ifIdsMap = new HashMap<>(systemIds.size());
		// 将查到的HTTP接口标识加入映射表
		for (String id : ifIds) {
			ifIdsMap.put(id, true);
		}
		// 第一条记录下标，不包含
		int startIndex = pageable.getPageNumber() * pageable.getPageSize();
		// 最后一条记录下标，包含
		int endIndex = startIndex + pageable.getPageSize();
		// 目标接口标识及检测结果集
		Map<String, String> targetIfIds = new HashMap<>(pageable.getPageSize());
		int total = 0;
		boolean withStatus = StringUtils.isNotBlank(param.getStatus()) ? true
				: false;
		// 筛选符合条件的数据
		for (Entry<String, String> entry : map.entrySet()) {
			// 判断检测状态是否符合条件
			if (withStatus && (!param.getStatus().equals(entry.getValue()))) {
				continue;
			}
			// 接口对当前用户不可见，过滤掉
			if (ifIdsMap.get(entry.getKey()) == null) {
				continue;
			}
			// 记录数增加
			total++;
			// 不是当前页的记录
			if (total <= startIndex || total > endIndex) {
				continue;
			}
			// 是当前页的记录，将其加入结果集
			targetIfIds.put(entry.getKey(), entry.getValue());
		}
		// 没有符合条件的记录，返回空集
		if (targetIfIds.keySet().isEmpty()) {
			return new PageImpl<HttpVo>(new ArrayList<>(0), pageable, 0);
		}
		// 根据接口标识获取接口信息
		List<Http> https = httpInterfaceService.getHttpInterfaceInfos(
				Arrays.asList(targetIfIds.keySet().toArray(
						new String[targetIfIds.size()])),
				AuditStatusType.AUDITED.ValueCode, EnableType.ENABLE.ValueCode);
		// 没有符合条件的记录，返回空集
		if (https == null || https.isEmpty()) {
			return new PageImpl<HttpVo>(new ArrayList<>(0), pageable, 0);
		}
		List<HttpVo> result = new ArrayList<>(https.size());
		// 转换结果集
		for (Http http : https) {
			HttpVo vo = new HttpVo();
			if (http.getFieldSystem() != null) {
				// 设置系统名称
				vo.setSystemName(http.getFieldSystem().getName());
				if (http.getFieldSystem().getField() != null) {
					// 设置领域名称
					vo.setFieldName(http.getFieldSystem().getField().getName());
				}
			}
			vo.setType(http.getType());
			vo.setName(http.getName());
			vo.setCreateTime(http.getCreateTime());
			vo.setAuditStatus(http.getAuditStatus());
			vo.setVersion(http.getVersion());
			vo.setStatus(targetIfIds.get(http.getId()));
			vo.setDesc(http.getDesc());
			vo.setRemark(http.getRemark());
			result.add(vo);
		}
		return new PageImpl<HttpVo>(result, pageable, total);
	}

	/**
	 * 循环测试接口可用性
	 * 
	 * @param https
	 *            ：接口集合
	 */
	private synchronized static void loop(Collection<Http> https) {
		if (https == null || https.isEmpty()) {
			return;
		}
		// 请空前一次的测试数据
		TEST_RESULT_COLLECTION.clear();
		https.forEach(http -> {
			try {
				boolean result = execute(http);
				// 转换结果，是否可用
				String target = EnableType.ENABLE.ValueCode;
				// // 清除数据
				// String delete = EnableType.DISABLE.ValueCode;
				if (!result) {
					target = EnableType.DISABLE.ValueCode;
					// delete = EnableType.ENABLE.ValueCode;
				}
				// 生成键列表
				List<String> keys = generateKeys(http, target);
				// // 生成清除键列表
				// List<String> delKeys = generateDelKeys(http, delete);
				// // 清除上次检测的数据
				// serviceUsabilityJedisProxy.hdel(delKeys,
				// http.getId(),
				// false);

				if (keys != null) {
					for (String key : keys) {
						if (StringUtils.isBlank(key)
								|| StringUtils.isBlank(http.getId())
								|| StringUtils.isBlank(target)) {
							continue;
						}
						String[] strs = { key, http.getId(), target };
						TEST_RESULT_COLLECTION.add(strs);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		});
		// 保存测试结果到REDIS
		try {
			ServiceUsabilityJedisProxy.hset(TEST_RESULT_COLLECTION);
		} catch (JsonProcessingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		TEST_RESULT_COLLECTION.clear();
	}

	/**
	 * 需要清除的数据的键值
	 * 
	 * @param http
	 * @param result
	 * @return
	 */
	private List<String> generateDelKeys(Http http, String result) {
		List<String> keys = new ArrayList<>(4);
		// 无分类存储：Key= REDIS_HASH_KEY_IF_USAB_TEST
		keys.add(Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST);
		// // 按检测状态存储：Key= HS_KEY_IF_USAB_TEST:{test result}
		// keys.add(Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST
		// + Constants4Redis.KEY_SEPARATOR + result);
		// if (http.getFieldSystem() != null
		// && http.getFieldSystem().getField() != null) {
		// // 按领域存储：Key= REDIS_HASH_KEY_IF_USAB_TEST:{fieldId}
		// keys.add(Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST
		// + Constants4Redis.KEY_SEPARATOR
		// + http.getFieldSystem().getField().getId());
		// // 按领域和检测状态存储：Key= HS_KEY_IF_USAB_TEST:{fieldId}:{test result}
		// keys.add(Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST
		// + Constants4Redis.KEY_SEPARATOR
		// + http.getFieldSystem().getField().getId()
		// + Constants4Redis.KEY_SEPARATOR + result);
		// // 按领域和系统存储：Key= REDIS_HASH_KEY_IF_USAB_TEST:{fieldId}:{systemId}
		// keys.add(Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST
		// + Constants4Redis.KEY_SEPARATOR
		// + http.getFieldSystem().getField().getId()
		// + Constants4Redis.KEY_SEPARATOR
		// + http.getFieldSystem().getId());
		// // 按领域、系统和检测状态存储：Key=
		// // REDIS_HASH_KEY_IF_USAB_TEST:{fieldId}:{systemId}:{test result}
		// keys.add(Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST
		// + Constants4Redis.KEY_SEPARATOR
		// + http.getFieldSystem().getField().getId()
		// + Constants4Redis.KEY_SEPARATOR
		// + http.getFieldSystem().getId()
		// + Constants4Redis.KEY_SEPARATOR + result);
		// }
		return keys;
	}

	/**
	 * 生成服务可用性测试键列表
	 * 
	 * @param http
	 *            ：接口
	 * @param result
	 *            ：检测结果
	 * @return
	 */
	private static List<String> generateKeys(Http http, String result) {
		List<String> keys = new ArrayList<>(4);
		// 无分类存储：Key= REDIS_HASH_KEY_IF_USAB_TEST
		keys.add(Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST);
		// // 按检测状态存储：Key= HS_KEY_IF_USAB_TEST:{test result}
		// keys.add(Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST
		// + Constants4Redis.KEY_SEPARATOR + result);
		// if (http.getFieldSystem() != null
		// && http.getFieldSystem().getField() != null) {
		// // 按领域存储：Key= REDIS_HASH_KEY_IF_USAB_TEST:{fieldId}
		// keys.add(Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST
		// + Constants4Redis.KEY_SEPARATOR
		// + http.getFieldSystem().getField().getId());
		// // 按领域和检测状态存储：Key= HS_KEY_IF_USAB_TEST:{fieldId}:{test result}
		// keys.add(Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST
		// + Constants4Redis.KEY_SEPARATOR
		// + http.getFieldSystem().getField().getId()
		// + Constants4Redis.KEY_SEPARATOR + result);
		// // 按领域和系统存储：Key= REDIS_HASH_KEY_IF_USAB_TEST:{fieldId}:{systemId}
		// keys.add(Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST
		// + Constants4Redis.KEY_SEPARATOR
		// + http.getFieldSystem().getField().getId()
		// + Constants4Redis.KEY_SEPARATOR
		// + http.getFieldSystem().getId());
		// // 按领域、系统和检测状态存储：Key=
		// // REDIS_HASH_KEY_IF_USAB_TEST:{fieldId}:{systemId}:{test result}
		// keys.add(Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST
		// + Constants4Redis.KEY_SEPARATOR
		// + http.getFieldSystem().getField().getId()
		// + Constants4Redis.KEY_SEPARATOR
		// + http.getFieldSystem().getId()
		// + Constants4Redis.KEY_SEPARATOR + result);
		// }
		return keys;
	}

	/**
	 * 根据查询条件，获取REDIS键值
	 * 
	 * @param param
	 * @return
	 */
	private String getKey(HttpInterfaceParam param) {
		// 默认为无分类查询
		String key = Constants4Redis.REDIS_HASH_KEY_IF_USAB_TEST;
		// // 添加领域
		// if (StringUtils.isNotBlank(param.getFieldId())) {
		// key += Constants4Redis.KEY_SEPARATOR + param.getFieldId();
		// }
		// // 添加系统
		// if (StringUtils.isNotBlank(param.getFieldSystemId())) {
		// key += Constants4Redis.KEY_SEPARATOR + param.getFieldSystemId();
		// }
		// // 添加状态
		// if (StringUtils.isNotBlank(param.getStatus())) {
		// key += Constants4Redis.KEY_SEPARATOR + param.getStatus();
		// }
		return key;
	}

	/**
	 * 执行测试
	 * 
	 * @param http
	 *            :接口信息
	 */
	private static boolean execute(Http http) {
		// 只能测试查找类型的接口
		if (StringUtils.isBlank(http.getOperationType())
				|| !IfOperationType.QUERY.ValueCode.equals(http
						.getOperationType())) {
			return false;
		}
		boolean result = false;
		try {
			// 组装测试数据
			IfType interfaceType = IfType.get(http.getType());
			// 没有设置请求类型，默认为GET请求类型
			IfReqType requestType = IfReqType.get(StringUtils.isNotBlank(http
					.getRequest()) ? http.getRequest()
					: IfReqType.GET.ValueCode);
			// 如果是SOAP类型接口，则设置请求类型为POST
			requestType = IfType.WS == interfaceType ? IfReqType.POST
					: requestType;
			DataFormateType
					.get(StringUtils.isNotBlank(http.getDataFormate()) ? http
							.getDataFormate() : DataFormateType.XML.ValueCode);
			// 格式化请求报文
			String msg = formatMsg(http, interfaceType);

			ServiceTestingParam param = new ServiceTestingParam();
			param.setAuthType(AuthencationModeType.NOAUTH.ValueCode);
			param.setContentType(http.getContentType());
			param.setDataFormate(http.getDataFormate());
			param.setInterfaceType(http.getType());
			param.setParameters(msg);
			param.setRequest(http.getRequest());
			param.setUrl(http.getPriUrl());
			param.setSoapActionUri(http.getSoapActionUri());
			// 进行测试
			Map<String, Object> map = ServiceTestingService.sendRequest(param,
					false);

			// 根据结果修改接口状态
			int code = (int) map.get(CommonUtils.RESPONDSE_STATUS_CODE);
			if (code == 200) {
				result = true;
			}
		} catch (Exception e) {
			result = false;
		}
		return result;
	}

	/**
	 * 格式化请求报文
	 * 
	 * @param http
	 *            ：接口信息
	 * @param ifType
	 *            :接口类型
	 * @return
	 */
	private static String formatMsg(Http http, IfType ifType) {
		// Web Service接口类型请求报文参数设置
		if (IfType.WS == ifType) {
			return formatSoap(http.getRequestMess(),
					http.getInterfaceParameter());
		}
		// REST接口类型请求报文参数设置
		if (IfType.REST == ifType) {
			return formatRest(http.getInterfaceParameter());
		}
		// 其它接口类型暂不支持，默认返回空值
		return "";
	}

	/**
	 * 格式化REST报文
	 * 
	 * @param parameters
	 * @return
	 */
	private static String formatRest(Set<IfParameter> parameters) {
		// 无参数，返回“{}”
		if (parameters == null || parameters.size() == 0) {
			return CommonUtils.JSON_START_SYMBOL + CommonUtils.JSON_END_SYMBOL;
		}
		StringBuilder builder = new StringBuilder();
		// 加入JSON开始标识
		builder.append(CommonUtils.JSON_START_SYMBOL);
		boolean first = true;
		for (IfParameter parameter : parameters) {
			// 非第一条记录，加入分隔符
			if (!first) {
				builder.append(CommonUtils.JSON_ENTRY_SEPARATOR);
			}
			// 加入键值对
			builder.append(parameter.getName())
					.append(CommonUtils.JSON_KEY_VALUE_SEPARATOR)
					.append(getDefaultParaValue(parameter.getDataType()));
			first = false;
		}
		// 加入JSON开始标识
		builder.append(CommonUtils.JSON_END_SYMBOL);
		return builder.toString();
	}

	/**
	 * 格式化SOAP报文
	 * 
	 * @param reqMess
	 *            ：报文
	 * @param parameters
	 *            :接口参数
	 * @return
	 */
	private static String formatSoap(String reqMess, Set<IfParameter> parameters) {
		if (parameters == null || parameters.size() == 0) {
			return reqMess;
		}
		Map<String, Object> map = new HashMap<String, Object>();
		for (IfParameter parameter : parameters) {
			if (StringUtils.isBlank(parameter.getName())) {
				continue;
			}
			// 存在默认值，则设置默认值
			if (StringUtils.isNotBlank(parameter.getDefaultValue())) {
				map.put(parameter.getName(), parameter.getDefaultValue());
				continue;
			}
			// 根据参数类型设置默认值
			map.put(parameter.getName(),
					getDefaultParaValue(parameter.getDataType()));
		}
		// 将默认参数加入请求报文
		return ServiceTestingService.formatSoapMsg(reqMess, map);
	}

	/**
	 * 设置默认值
	 * 
	 * @param type
	 *            :数据类型
	 * @return
	 */
	private static String getDefaultParaValue(String type) {
		// 设置字符串类型默认值
		if (IfParameterType.STRING.ValueCode.equals(type)) {
			return "test";
		}
		// 设置字整型类型默认值
		if (IfParameterType.INT.ValueCode.equals(type)) {
			return 1 + "";
		}
		// 设置日期类型默认值
		if (IfParameterType.DATE.ValueCode.equals(type)) {
			return FASTDATEFORMAT_YYYYMMDDHHMMSS.format(new Date());
		}
		// 设置单精度浮点类型默认值
		if (IfParameterType.FLOAT.ValueCode.equals(type)) {
			return 1f + "";
		}
		// 设置双精度浮点类型默认值
		if (IfParameterType.DOUBLE.ValueCode.equals(type)) {
			return 1.0 + "";
		}
		// 其它类型默认返回值
		return "";
	}
}
